package br.unb.statistic.distribution;
//TODO review this class, doesnt seem to be used!
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.HashMap;
import br.unb.utils.LogFile;
import br.unb.utils.LogPrinter;

public class MWMGenerator extends PacketsPerIntervalGenerator {

    //Parameters
    private long numSamples;
    private double hurst;
    private double variance;
    private double mean;
    //File
    private LogFile simulationFile;
    private LogPrinter simulationPrinter;
    private LineNumberReader lnr;

    public MWMGenerator() {
    }

    public MWMGenerator(String name, double mean, double variance, double hurst, long samples) throws IOException {
        initGen(name, mean, variance, hurst, samples, 1.0);
    }

    public MWMGenerator(String name, double mean, double variance, double hurst, long samples, double interval) throws IOException {
        initGen(name, mean, variance, hurst, samples, interval);
    }

    private void initGen(String name, double mean, double variance, double hurst, long samples, double interval) throws IOException {

        //Parameters
        this.hurst = hurst;
        this.mean = mean;
        this.interval = interval;
        this.variance = variance;
        this.numSamples = samples;
        //File
        this.simulationFile = new LogFile(LogFile.Type.SIMULATE, name);
        this.simulationPrinter = new LogPrinter(simulationFile);
        this.lnr = new LineNumberReader(new InputStreamReader(new FileInputStream(this.simulationFile)));

        long steps = (long) Math.ceil(Math.log(samples) / Math.log(2));
        double[] variances = calculateWaveletVariances(steps);
        simulation(variances, steps);
    }

    private void simulation(double[] variances, long steps) {
        double[] A = calculateA(variances);
        double[] values = {mean * Math.pow(2, steps)};
        double[] U;

        for (int i = 0; i < steps; i++) {
            U = upScale(A[i], values);
            values = U;
        }

        //values = calculateW(values, A[A.length -1]);

        for (int i = 0; i < values.length; i++) {
            values[i] = Math.pow(2, -(steps) / 2) * values[i];
        }

        for (int i = 0; i < numSamples; i++) {
            simulationPrinter.write(Double.toString(values[i]));
        }
    }

    private double[] calculateWaveletVariances(long steps) {
        double[] variances = new double[(int) steps];
        for (int j = 0; j < steps; j++) {
            variances[j] = variance * Math.pow(2, (2 * hurst - 1) * ((-1 * j) - 1)) * (2 - Math.pow(2, 2 * hurst - 1));
        }
        return variances;
    }

    private double[] upScale(double A, double[] values) {

        double[] U = new double[2 * values.length];

        for (int i = 0; i < values.length; i++) {
            U[2 * i] = (values[i] + values[i] * A) / Math.sqrt(2);
            U[2 * i + 1] = (values[i] - values[i] * A) / Math.sqrt(2);
        }
        return U;
    }

    private double[] calculateA(double[] variances) {

        double[] multiplierA = new double[variances.length];
        double[] p = new double[variances.length];
        p[0] = (1 / (2 * (Math.pow(2, (2 - 2 * hurst)) - 1))) - 0.5;

        BetaDistribution beta = new BetaDistribution(p[0], p[0], -1, 1);
        multiplierA[0] = beta.rand();

        for (int i = 1; i < multiplierA.length; i++) {
            p[i] = ((variances[i - 1] * (p[i - 1] + 1)) / (2 * variances[i])) - 0.5;
            beta.setABParam(p[i], p[i]);
            multiplierA[i] = beta.rand();
        }

        return multiplierA;
    }

    public double[] calculateW(double[] U, double A) {
        double[] result = new double[U.length];

        for (int i = 0; i < result.length; i++) {
            result[i] = U[i] * A;
        }
        return result;
    }

    public double rand() {
        try {
            return Double.parseDouble(lnr.readLine());
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Double.NaN;
    }

    public void setParameters(HashMap parameters) {
        // TODO Auto-generated method stub (empty)
    }
    /*
    public static void main(String args[]) {
    try {
    MWMGenerator gen = new MWMGenerator("mwmteste", 500, 2000, 0.85, 1000);
    gen.rand();
    } catch (IOException e) {
    e.printStackTrace();
    }

    }
     */
}
